#####################################################################
# Product:      KnowledgeEngine (Plone Edition)
#
# File:         PloneGroupProviderService.py
#
# Copyright:    Copyright (c) 2004, Faulkner Technologies
#
# Author:       Brian Oliver
#
# Description:  Defines a GroupProviderService that utilises Plone
#               and GRUF (GRoup User Folders) to determine the
#               available groups.
#
# See Also:     Service, GroupProviderService
#####################################################################

#####################################################################
## KnowledgeEngine Library Imports
from Products.KnowledgeEngine.Common import *
from Products.KnowledgeEngine.Core import *
from Products.KnowledgeEngine.GroupProviderService import GroupProviderServiceInfo, GroupProviderService
from Products.KnowledgeEngine.Group import Group

#####################################################################
## Plone Library Imports
from Products.CMFCore.utils import getToolByName

#####################################################################
## Zope Library Imports
# (none)
from AccessControl import ClassSecurityInfo

#####################################################################
## Python Library Imports
# (none)

#####################################################################
## PloneGroupProviderService Class Definition
class PloneGroupProviderServiceInfo(GroupProviderServiceInfo):
    """
        Defines the the group provider service meta-information
    """

    #---------------------------------------------------------------#

    def getClassName(self):
        """
            Returns the name of the class for which
            we are providing information.
        """
        return 'PloneGroupProviderService'


    #---------------------------------------------------------------#

    def getDisplayName(self):
        """
            Return a friendly name for the class instances
            (this is used for displaying in lists etc)
        """

        return "KnowledgeEngine: Plone Group Provider Service (uses GRUF)"

    #---------------------------------------------------------------#

    def getServiceName(self):
        """
            ABSTRACT: Return the lookup-identity.

            This method must be overriden to provide a unique service name
            (this name is used to set the zope id of the service
            when created)
        """

        return "knowledgeengine_groupproviderservice"

#####################################################################
## PloneGroupProviderService Class Definition
class PloneGroupProviderService(GroupProviderService):
    """
        Defines a tool that may be used to manage Knowledge Engine
        groups
    """

    #---------------------------------------------------------------#

    #security declaration
    security = ClassSecurityInfo()

    #---------------------------------------------------------------#

    #ABSTRACT TRANSIENT: a ServiceInfo implementation instance to provide info about this service
    _SERVICEINFO = PloneGroupProviderServiceInfo()

    #---------------------------------------------------------------#

    def __init__(self):
        """
            Constructor for the object
        """

        #this is a stateless mix-in
        pass

    #---------------------------------------------------------------#

    def getGroupUserFolder(self):
        """
            Attempts to locate the Group User Folder which is
            typically the 'acl_users' folder.

            If a Group User Folder can't be found None is returned...
        """

        #attempt to get the acl_users
        groupUserFolder = self.acl_users

        #TODO: we could cache the groupUserFolder here... might improve performance

        #is the user folder a gruf or PAS instance?
        if hasattr(groupUserFolder, 'getGRUFVersion') or hasattr(groupUserFolder, 'searchGroups'):
            return groupUserFolder
        else:
            return None

    #---------------------------------------------------------------#

    def getGroupTool(self):
        """
            Attempts to locate the Group tool which is typically
            portal_groups
        """

        #attempt to get portal_groups
        groupTool = getToolByName(self, 'portal_groups')

        return groupTool

    #---------------------------------------------------------------#

    def getGroup(self, groupIdentity):
        """
            Returns a Knowledge Engine Group instance for the
            group with the provided identity.

            The groupIdentity is a unique string that identifies the group
            with for the service implementation.  Typically this is
            the group name (but is actually implementation dependant)

            Returns None if the group is unknown.

            RETURN TYPE: Group
        """

        #find the group user folder
        groupUserFolder = self.getGroupUserFolder()

        #find the group... if it exists
        if groupUserFolder is not None:
            #ploneGroup = groupUserFolder.getGroup(groupIdentity, prefixed = false)
            ploneGroup = groupUserFolder.getGroup(groupIdentity)
            if ploneGroup is not None:
                return Group(groupIdentity)
            else:
                return None
        else:
            #no groupUserFolder is available...
            return None

    #---------------------------------------------------------------#

    def getGroups(self):
        """
            Returns a list of groups that this service provides

            RETURN TYPE: [Group]
        """

        #find the group user folder
        groupUserFolder = self.getGroupUserFolder()

        # jamesd 2006-08-17 - Work with both Pas and Gruf
        getGroupNames = getattr(groupUserFolder, 'getGroupNames', getattr(groupUserFolder, 'searchGroups', None))
                            
        print "GN: " % getGroupNames()

        #return all of the available the groups
        if groupUserFolder is not None:
            # jamesd 2006-05-10 GroupUserFolder in Plone 2.1 no longer accepts 'prefixed' argument
            return [self.getGroup(groupName) for groupName in getGroupNames()]
        else:
            #no groupUserFolder is available...
            return []

    #---------------------------------------------------------------#

    def getParentGroupsForActor(self, actorIdentity):
        """
            Returns a list of the immediate groups to which the
            specified actor belongs.

            RETURN TYPE: [Group]
        """

        #find the group user folder
        groupUserFolder = self.getGroupUserFolder()

        #attempt to get the actor (user or group) parent groups
        if groupUserFolder is not None:

            #is the actor a group (GRUFUser)?
            grufUser = groupUserFolder.getGroup(actorIdentity)
            if grufUser is None:
                #no... the actor is probably a user
                grufUser = groupUserFolder.getUser(actorIdentity)

            #do we have a gruf object (user or group)?
            if grufUser is not None:

                #create a KE Group for each group returned
                groups = [self.getGroup(grufGroupName) for grufGroupName in grufUser.getGroupsWithoutPrefix(no_recurse = true)]

            else:
                #couldn't find the actor.. so there are no parents
                groups = []
        else:
            #no groupUserFolder is available...
            groups = []

        return groups

    #---------------------------------------------------------------#

    def getGroupsForActor(self, actorIdentity):
        """
            Returns a list of all of the groups to which a specified
            actor either directly or indirectly belongs.

            RETURN TYPE: [Group]
        """

        #find the group user folder
        groupUserFolder = self.getGroupUserFolder()

        #attempt to get the actor (user or group) parent groups
        if groupUserFolder is not None:

            #is the actor a group (GRUFUser)?
            grufUser = groupUserFolder.getGroup(actorIdentity)
            if grufUser is None:
                #no... the actor is probably a user
                grufUser = groupUserFolder.getUser(actorIdentity)
            
            #do we have a gruf object (user or group)?
            if grufUser is not None:
                
                #create a KE Group for each group returned
                if hasattr(grufUser, 'getGroupsWithoutPrefix'):
                    groups = [self.getGroup(grufGroupName) for grufGroupName in grufUser.getGroupsWithoutPrefix(no_recurse = false)]
                else:
                    groups = [self.getGroup(grufGroupName) for grufGroupName in grufUser.getGroups()]
                    
            else:
                #couldn't find the actor.. so there are no parents
                groups = []
        else:
            #no groupUserFolder is available...
            groups = []


        return groups

    #---------------------------------------------------------------#

    def getGroupsManagedByActor(self, actorIdentity):
        """
            Returns a list of all of the groups that the
            specified actor belongs manages.

            NOTE: This is different from the groups to which
            an actor belongs.  This feature is only implemented
            by GRUFAccessUserFolder.

            RETURN TYPE: [Group]
        """

        #find the group user folder
        groupUserFolder = self.getGroupUserFolder()

        if hasattr(self.acl_users, 'getPasGAPlugin'):
            plugin = self.acl_users.getPasGAPlugin()
            if plugin:
                groupIdentities = plugin.getGroupAccessForUser( actorIdentity )
                groups = [self.getGroup(groupIdentity) for groupIdentity in groupIdentities]
                return groups


        #does the folder provide GGA group management?
        if hasattr(groupUserFolder, 'getManagableGroups'):

            #get the group identities and remove prefixing if in place
            #(sometimes the Group Access managers prefix names of groups... we tidy them up here)
            groupIdentities = []
            manageableGroups = groupUserFolder.getManagableGroups(actorIdentity)
            
            #ensure we have some manageable groups
            if manageableGroups is not None:
                for groupIdentity in groupUserFolder.getManagableGroups(actorIdentity):
                    #ensure that we have a real group identity
                    if groupIdentity is not None:
                        if groupIdentity.startswith('group_'):
                            groupIdentity = groupIdentity[6:]

                        groupIdentities.append(groupIdentity)

            #now resolve the managed group identities into group objects
            groups = [self.getGroup(groupIdentity) for groupIdentity in groupIdentities]
            return groups


        

        else:
            #if the internal group manager doesn't support 'group management' we return no groups!
            return []

    #---------------------------------------------------------------#

    security.declarePublic('getUserIdentitiesInGroup')
    def getUserIdentitiesInGroup(self, groupIdentity):
        """
            Return a list the identities of users that belong to
            the specified group (does not include users in sub-groups)

            NOTE: this method also returns the identities of subgroups
            which may or may not be needed. These Identities are prefixed
            with group_ so you can identify them if you need to

            NOTE: it appears not to be safe to just use the getGroupMemberIds()
            method provided by GRUF as it breaks when finding sub groups. So we
            have to itterate over the Group Objects and get the Id explicitly.

            RETURNS: [String]
        """

        #find the group tool first
        groupTool = self.getGroupTool()

        #now get the Plone Group object
        ploneGroup = groupTool.getGroupById(groupIdentity)

        if ploneGroup is not None:
            userIdentities = [i.getId() for i in ploneGroup.getGroupMembers()]
            return userIdentities

        return []

    #---------------------------------------------------------------#


#####################################################################
## Class Initialisation
# (none)

# Register Service Information with the Knowledge Engine
registerServiceClass(PloneGroupProviderService)
